package com.uit3j.cloud.search.manager;

import com.uit3j.cloud.api.dto.EsPageDTO;
import com.uit3j.cloud.api.dto.HotSearchDTO;
import com.uit3j.cloud.api.vo.EsPageVO;
import com.uit3j.cloud.api.vo.search.HotSearchVO;
import com.uit3j.cloud.common.exception.Uit3jcloudException;
import com.uit3j.cloud.common.util.Json;
import com.uit3j.cloud.search.constant.EsIndexEnum;
import com.uit3j.cloud.search.constant.SearchTypeEnum;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;


/**
 * @author tan
 * @date 2020/11/16
 */
@Component
public class HotSearchManager {

    private static final Logger log = LoggerFactory.getLogger(HotSearchManager.class);


    @Autowired
    private RestHighLevelClient restHighLevelClient;

    /**
     * 通过搜索信息分页搜索es数据并聚合返回的信息
     *
     * @param pageDTO      分页数据
     * @param hotSearchDTO 商品搜索条件
     * @return 搜索结果
     */
    public EsPageVO<HotSearchVO> page(EsPageDTO pageDTO, HotSearchDTO hotSearchDTO) {
        hotSearchDTO.setSearchType(SearchTypeEnum.APP.value());
        SearchResponse response = pageSearchResult(pageDTO, hotSearchDTO, Boolean.TRUE);
        return buildSearchResult(pageDTO,response);
    }

    /**
     * 通过搜索信息分页搜索es数据的信息
     *
     * @param pageDTO      分页数据
     * @param hotSearchDTO 商品搜索条件
     * @return 搜索结果
     */
    public EsPageVO<HotSearchVO> simplePage(EsPageDTO pageDTO, HotSearchDTO hotSearchDTO) {
        hotSearchDTO.setSearchType(SearchTypeEnum.APP.value());
        SearchResponse response = pageSearchResult(pageDTO, hotSearchDTO, Boolean.FALSE);
        return buildSearchResult(pageDTO,response);
    }


    /**
     * 通过搜索信息分页搜索es数据的信息
     * @param pageDTO 分页数据
     * @param hotSearchDTO 商品搜索条件
     * @param isAgg true:聚合搜索  false:非聚合搜索  null:非聚合搜索
     * @return 搜索结果
     */
    private SearchResponse pageSearchResult(EsPageDTO pageDTO, HotSearchDTO hotSearchDTO, Boolean isAgg) {
        //1、准备检索请求
        SearchRequest searchRequest = buildSearchRequest(pageDTO, hotSearchDTO, isAgg);
        SearchResponse response = null;

        try {
            //2、执行检索请求
            response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            log.debug("搜索返回结果：" + response.toString());
        } catch (IOException e) {
            log.error(e.toString());
            throw new Uit3jcloudException("搜索服务出了点小差，请稍后再试", e);
        }
        return response;
    }

    /**
     * 构建结果数据
     */
    private EsPageVO<HotSearchVO> buildSearchResult(EsPageDTO pageDTO, SearchResponse response) {
        EsPageVO<HotSearchVO> esPageVO = new EsPageVO<>();

        //1、返回的所有查询到的商品
        SearchHits hits = response.getHits();

        List<HotSearchVO> hotSearchs = getSpuListByResponse(hits.getHits());
        esPageVO.setList(hotSearchs);


        //===============分页信息====================//
        //总记录数
        long total = hits.getTotalHits().value;
        esPageVO.setTotal(total);
        // 总页码
        int totalPages = (int)total % pageDTO.getPageSize() == 0 ?
                (int)total / pageDTO.getPageSize() : ((int)total / pageDTO.getPageSize() + 1);
        esPageVO.setPages(totalPages);
        return esPageVO;
    }

    /**
     * 从es返回的数据中获取spu列表
     * @param hits es返回的数据
     * @return
     */
    public List<HotSearchVO> getSpuListByResponse(SearchHit[] hits) {
        List<HotSearchVO> hotSearchs = new ArrayList<>();
        for (SearchHit hit : hits) {
            HotSearchVO hotSearchVO = Json.parseObject(hit.getSourceAsString(), HotSearchVO.class);
            hotSearchs.add(hotSearchVO);
        }
        return hotSearchs;
    }



    /**
     * 准备检索请求
     * @param pageDTO 分页参数
     * @param param 搜索参数
     * @param isAgg true:聚合搜索  false:非聚合搜索  null:非聚合搜索
     * @return
     */
    private SearchRequest buildSearchRequest(EsPageDTO pageDTO,HotSearchDTO param, Boolean isAgg) {

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //分页
        if (Objects.nonNull(pageDTO)) {
            searchSourceBuilder.from((pageDTO.getPageNum()-1)*pageDTO.getPageSize());
            searchSourceBuilder.size(pageDTO.getPageSize());
        }

        log.debug("构建的DSL语句 {}",searchSourceBuilder.toString());

        return new SearchRequest(new String[]{EsIndexEnum.HOTSEARCH.value()},searchSourceBuilder);
    }









    /**
     * 热搜管理分页搜索es数据的信息
     * @param pageDTO 分页数据
     * @param hotSearchDTO 热搜管理搜索条件
     * @return 搜索结果
     */
    public EsPageVO<HotSearchVO> adminPage(EsPageDTO pageDTO, HotSearchDTO hotSearchDTO) {
        EsPageVO<HotSearchVO> result = new EsPageVO<>();
        SearchResponse response = pageSearchResult(pageDTO, hotSearchDTO, Boolean.FALSE);
        // 商品信息
        result.setList(buildSpuAdminList(response));
        // 分页信息
        buildSearchPage(pageDTO, result, response);
        return result;
    }


    /**
     * 从es返回的数据中获取spu列表
     * @param response es返回的数据
     * @return
     */
    public List<HotSearchVO> buildSpuAdminList(SearchResponse response) {
        List<HotSearchVO> hotSearchs = new ArrayList<>();
        for (SearchHit hit : response.getHits()) {
            String json = hit.getSourceAsString();
            HotSearchVO hotSearchVO = Json.parseObject(json, HotSearchVO.class);
            hotSearchs.add(hotSearchVO);
        }
        return hotSearchs;
    }


    /**
     * 构建分页数据
     * @param pageDTO
     * @param esPageVO
     * @param response
     */
    private void buildSearchPage(EsPageDTO pageDTO, EsPageVO<?> esPageVO, SearchResponse response) {
        //总记录数
        long total = response.getHits().getTotalHits().value;
        esPageVO.setTotal(total);
        // 总页码
        int totalPages = (int)total % pageDTO.getPageSize() == 0 ?
                (int)total / pageDTO.getPageSize() : ((int)total / pageDTO.getPageSize() + 1);
        esPageVO.setPages(totalPages);
    }
}
